<script lang="ts">
import {shallowReactive} from 'vue'
import {Command, createStage, Stage} from "@rockyf/easy-design";
import options from "./options.ts";
import UndoManager from "./undo-manager.ts";
import label from "./nodes/label.tsx";
import image from "./nodes/image.tsx";
import chart from "./nodes/chart.tsx";
import options2 from "./options2.ts";
import {NodeWrapper} from "@rockyf/easy-design";
import '@rockyf/easy-design/dist/style.css'

import type Node from 'element-plus/es/components/tree/src/model/node'
import type {DragEvents} from 'element-plus/es/components/tree/src/model/useDragNode'
import type {
  AllowDropType,
  NodeDropType,
} from 'element-plus/es/components/tree/src/tree.type'

const THEME_KEY = 'easy-design-theme'
const images = [
  'imgs/1.png',
  'imgs/2.png',
  'imgs/3.gif',
]

const undoManager = new UndoManager()

const nodeImplFactoryMap = {
  'label': label,
  'image': image,
  'chart': chart,
}

export default {
  name: "App",
  data() {
    return {
      stage: undefined as Stage | undefined,
      nodeTree: undefined as NodeWrapper[] | undefined,
      undoable: false,
      redoable: false,
      theme: localStorage.getItem(THEME_KEY) || 'dark',
      hoverNodeId: undefined as number | undefined,
      selections: [] as number[],
      nodeTypeToAdd: Object.keys(nodeImplFactoryMap)[0],
    }
  },
  computed: {
    treeProps() {
      return {
        children: 'children',
        label(data: any) {
          return data.nodeData.label
        },
      }
    },
    nodeImplFactoryMap() {
      return nodeImplFactoryMap
    },
  },
  watch: {
    theme(v: string) {
      this.stage!.changeTheme(v)
      localStorage.setItem(THEME_KEY, v)
    }
  },
  mounted() {
    const stage = createStage({
      ...options,
      theme: this.theme,
      nodeImplFactoryMap,
      onHistoryChange: this.onHistoryChange,
      onHoverNode: this.onHoverNode,
      onSelectionChange: this.onSelectionChange,
      onAddNode: this.updateNodeTree,
      onRemoveNode: this.updateNodeTree,
    })
    this.stage = shallowReactive(stage)
    console.log(this.stage)

    this.updateNodeTree()
  },
  beforeUnmount() {
    this.stage?.destroy()
  },
  methods: {
    async updateNodeTree() {
      this.nodeTree = []
      await this.$nextTick()
      this.nodeTree = this.stage!.root.children
    },
    onHoverNode(node?: NodeWrapper) {
      this.hoverNodeId = node?.id
      //console.log('onHoverNode:', node?.id, node)
    },
    onSelectionChange(selections: number[]) {
      this.selections.splice(0)
      this.selections.push(...selections)
      console.log('onSelectionChange:', selections)
    },
    onHistoryChange(command: Command) {
      undoManager.add(command)
      this.updateUndoRedoState()
    },
    clickUndo() {
      undoManager.undo()
      this.updateUndoRedoState()
    },
    clickRedo() {
      undoManager.redo()
      this.updateUndoRedoState()
    },
    clickClearHistory() {
      undoManager.clear()
      this.updateUndoRedoState()
    },
    updateUndoRedoState() {
      this.undoable = undoManager.hasUndo()
      this.redoable = undoManager.hasRedo()
    },
    getNodeClass(data: any) {
      const {hoverNodeId, selections} = this
      const hover = data.id === hoverNodeId
      const selected = selections.includes(data.id)
      const el = this.$refs['node_' + data.id] as HTMLElement
      hover ? el?.parentElement?.classList.add('hover') : el?.parentElement?.classList.remove('hover')
      selected ? el?.parentElement?.classList.add('selected') : el?.parentElement?.classList.remove('selected')
      return {hover, selected}
    },
    onMouseenterNode(data?: any) {
      //console.log('onMouseenterNode:', data?.id)
      this.stage!.setHoverNode(data?.id)
    },
    onClickNode(node: any, data: any, event: MouseEvent) {
      //console.log('onMouseenterNode:', data?.id)
      if (this.stage) {
        if (event.shiftKey) {
          //todo shift select
        } else {
          this.stage.commands.selectNode(data, {
            multiMode: event.metaKey,
          })
        }
      }
    },
    async clickAddNode() {
      const {nodeTypeToAdd} = this
      if (this.stage) {
        const props: any = {}
        const bounds = {x: 0, y: 0, w: 0, h: 0}
        switch (nodeTypeToAdd) {
          case 'label':
            bounds.w = 100
            bounds.h = 20
            props.text = 'label_' + Date.now().toString().substring(9)
            break
          case 'image':
            bounds.w = 100
            bounds.h = 100
            props.src = images[Math.floor(Math.random() * 3)]
            break
          case 'chart':
            bounds.w = 200
            bounds.h = 150
            let count = Math.floor(Math.random() * 4) + 4
            props.options = {
              backgroundColor: 'transparent',
              tooltip: {},
              grid: {
                left: '3%',
                right: '4%',
                top: '18%',
                bottom: '3%',
                containLabel: true
              },
              legend: {
                data: ['销量']
              },
              xAxis: {
                data: new Array(count).fill(0).map(() => Math.floor(Math.random() * 0xFF).toString(16).toUpperCase())
              },
              yAxis: {},
              series: [
                {
                  name: '销量',
                  type: 'bar',
                  data: new Array(count).fill(0).map(() => Math.floor(Math.random() * 0xFF))
                }
              ]
            }
            break
        }
        this.stage.commands.addNode({
          nodeType: nodeTypeToAdd,
          label: nodeTypeToAdd,
          bounds,
          props,
        })
      }
    },
    clickDeleteNode(data: any) {
      this.stage?.commands.removeNode(data.id)
    },
    allowDrop(draggingNode: Node, dropNode: Node, type: AllowDropType) {
      return type !== "inner" || dropNode.data.nodeData.nodeType === 'group'
    },
    allowDrag(draggingNode: Node) {
      return true
    },
    handleDragEnd(draggingNode: Node, dropNode: Node, type: AllowDropType, e: DragEvents) {
      console.log('drag end:', draggingNode.data)
    },
  }
};
</script>

<template>
  <div class="app">
    <div class="space-x-2 m-b-2 text-sm">
      <label>
        Theme
        <el-select v-model="theme" size="small" style="width: 100px;">
          <el-option value="light">Light</el-option>
          <el-option value="dark">Dark</el-option>
        </el-select>
      </label>
      <label>
        CommandManager
        <el-button size="small" @click="clickUndo" :disabled="!undoable">Undo</el-button>
        <el-button size="small" @click="clickRedo" :disabled="!redoable">Redo</el-button>
        <el-button size="small" @click="clickClearHistory">Clear</el-button>
      </label>
      <label>
        Node
        <el-button size="small" @click="clickAddNode">Add</el-button>
        <el-select v-model="nodeTypeToAdd" size="small" style="width: 100px;">
          <el-option
            v-for="(_, type) in nodeImplFactoryMap"
            :value="type"
            :key="type">
            {{ type }}
          </el-option>
        </el-select>
      </label>
    </div>
    <div class="flex space-x-2">
      <el-scrollbar class="w-50 !h-110 border border-solid border-gray box-border">
        <el-tree
          class="w-full hierarchy-tree select-none"
          :data="nodeTree"
          node-key="id"
          :props="treeProps"
          :expand-on-click-node="false"
          @mouseleave="onMouseenterNode()"
          draggable
          :allow-drop="allowDrop"
          :allow-drag="allowDrag"
          @nodeDragEnd="handleDragEnd"
        >
          <template #default="{ node, data }">
            <div
              :ref="'node_' + data.id"
              :class="getNodeClass(data)"
              class="flex-1 flex items-center p-r-2"
              @mouseenter="onMouseenterNode(data)"
              @click="onClickNode(node, data, $event)"
            >
              <span class="flex-1">{{ data.id }}:{{ node.label }}</span>
              <el-link type="danger" :underline="false" @click.stop="clickDeleteNode(data)">删除</el-link>
            </div>
          </template>
        </el-tree>
      </el-scrollbar>
      <div id="container" class="flex-1 h-110"></div>
    </div>
  </div>
</template>

<style lang="scss">
.hierarchy-tree {
  .el-tree-node__content {
    border: 1px solid transparent;
    box-sizing: border-box;

    &.hover, &:hover {
      border-color: var(--el-color-primary);
      background-color: var(--el-tree-node-hover-bg-color);
    }

    &.selected {
      background-color: var(--el-color-primary-light-8) !important;
    }
  }
}
</style>
